package com.googlecode.mhtetris.tetris
{
	import away3d.containers.ObjectContainer3D;
	import away3d.materials.ColorMaterial;
	import away3d.materials.MaterialBase;
	import away3d.primitives.Plane;
	
	import caurina.transitions.Tweener;
	
	import com.googlecode.mhtetris.Main3D;
	
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.TimerEvent;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	
	/**
	 * Classe représentant la grille d'un jeu Tetris
	 */	
	public class Tetris3D extends ObjectContainer3D
	{

		/* --- constantes --- */
		public static var LARGEUR_GRILLE_PX:int = 250;
		public static var NB_BLOCK_LARGEUR:int = 10;
		public static var HAUTEUR_GRILLE_PX:int = LARGEUR_GRILLE_PX << 1;
		public static var NB_BLOCK_HAUTEUR:int = NB_BLOCK_LARGEUR << 1;
		public static var S_LARGEUR_BLOCK_PX:int = LARGEUR_GRILLE_PX / NB_BLOCK_LARGEUR;
		public static var LARGEUR_BLOCK_PX:int = LARGEUR_GRILLE_PX / NB_BLOCK_LARGEUR;
		
		private var main:Main3D;
		
		public var USER_TWEENER:Boolean = false;
		
		public var isListeningKeyboard:Boolean = false;
		
		//public const BLOCKS:Array = ["RlDL", "UdDL", "UdDD", "LDR", "UdLrR"];
		//public const BLOCKS:Array = ["RRllLL"];
		public const BLOCKS:Array = ["RlDL", "UdDL", "UdDD", "LDR", "UdLrR", "LrDR", "ULrdD"];
		public const TIMER_DELAY:int = 800;
		
		/* --- attributs privées --- */
		
		/**
		 * formes de la grille (en incluant la forme courante)
		 */
		public var lesFormes:Vector.<GBlock>;
		
		/**
		 * forme que le joueur est en train de controler
		 */
		private var currentGb:GBlock;
		
		/**
		 * timer permettant de définir le temps entre deux mouvements de bloc
		 */
		private var timer:Timer;
		
		private var _grille:Grid;
		
		private var _nextGBlock:String;
		
		
		/* --- constructeurs --- */
		/**
		 * permet d'initialiser une grille de tetris
		 */
		public function Tetris3D(leStage:Stage, _main:Main3D)
		{		
			
			main = _main;
			
			// on créé la grille
			grille = new Grid(NB_BLOCK_LARGEUR, NB_BLOCK_HAUTEUR, ((Tetris3D) (this)));
			
			lesFormes = new Vector.<GBlock>();
			
			// les limites de la grille
			/*graphics.beginFill(0xDDDDDD);
			graphics.lineStyle(1,0xFF0000);
			graphics.drawRect(0,0,LARGEUR_GRILLE_PX, HAUTEUR_GRILLE_PX);*/
			var matBase:ColorMaterial = new ColorMaterial(0xA5EC79);
			matBase.ambient = 50;
			matBase.bothSides = true;
			matBase.lights = Main3D.lights;
			matBase.shadowMethod = Main3D.shadow;
			
			// plan 1
			var plane1:Plane = new Plane(matBase, LARGEUR_GRILLE_PX, 20);
			plane1.x = LARGEUR_GRILLE_PX/2;
			plane1.y = 0;
			plane1.z = 0;
			plane1.rotationX = 90;
			addChild(plane1);					
			// plan 2
			var plane2:Plane = new Plane(matBase, HAUTEUR_GRILLE_PX, 20);
			plane2.x = 0;
			plane2.y = HAUTEUR_GRILLE_PX/2;
			plane2.z = 0;
			plane2.rotationX = 90;
			plane2.rotationZ = 90;
			addChild(plane2);					
			// plan 3
			var plane3:Plane = new Plane(matBase, HAUTEUR_GRILLE_PX, 20);
			plane3.x = LARGEUR_GRILLE_PX;
			plane3.y = HAUTEUR_GRILLE_PX/2;
			plane3.z = 0;
			plane3.rotationX = 90;
			plane3.rotationZ = 90;
			addChild(plane3);					
			// plan 4
			var plane4:Plane = new Plane(matBase, LARGEUR_GRILLE_PX, 20);
			plane4.x = LARGEUR_GRILLE_PX/2;
			plane4.y = HAUTEUR_GRILLE_PX;
			plane4.z = 0;
			plane4.rotationX = 90;
			addChild(plane4);		
			// timer
			timer = new Timer(TIMER_DELAY);
			timer.addEventListener(TimerEvent.TIMER, timerHandler);
			timer.start();
			
			
			nextGBlock = BLOCKS[Math.floor(Math.random()*BLOCKS.length)];
			generateBlock();
			
			// gestion des évènements clavier
			leStage.addEventListener(KeyboardEvent.KEY_DOWN, keyHandler);
		}
		
		/* --- fonctions --- */
		
		
		/**
		 * hash du prochain gblock que l'utilisateur va contrôler
		 */
		public function get nextGBlock():String
		{
			return _nextGBlock;
		}

		/**
		 * @private
		 */
		public function set nextGBlock(value:String):void
		{
			_nextGBlock = value;
		}

		/**
		 * grille représentant la situation du tetris
		 */
		public function get grille():Grid
		{
			return _grille;
		}

		/**
		 * @private
		 */
		public function set grille(value:Grid):void
		{
			_grille = value;
		}

		/**
		 * ajoute un block à la grille
		 */
		private function generateBlock():void {
			
			
			var gb:GBlock = new GBlock(nextGBlock, ((Tetris3D) (this)));
			//lesFormes.push(gb);
			addChild(gb);
			currentGb = gb;						
			currentGb.posX = currentGb.posY = 4;
			gb.x = currentGb.posX*LARGEUR_BLOCK_PX;
			gb.y = currentGb.posY*LARGEUR_BLOCK_PX;
			
			
			nextGBlock = BLOCKS[Math.floor(Math.random()*BLOCKS.length)];
			
			dispatchEvent(new Event("blockChanged"));
		}
		
		/**
		 * fonction appelée à chaque fois que le timer déclenche un évènement
		 */
		private function timerHandler(event:TimerEvent):void {
			
			//if (currentGb.y + currentGb.blockHeight*LARGEUR_BLOCK_PX/*currentGb.height*/ > NB_BLOCK_HAUTEUR*LARGEUR_BLOCK_PX ) {
			if (false && currentGb.posY+currentGb.blockHeight >= NB_BLOCK_HAUTEUR) {
				// on est en bas
				positionneCurrentGBlock();
				generateBlock();
			} else {
				move(currentGb, "D");
			}
			
		}
		
		/**
		 * fonction appelée chaque fois que l'utilisateur appuie sur une touche du clavier
		 */
		private function keyHandler(event:KeyboardEvent):void {
			
			//if (currentGb.y + currentGb.blockHeight*LARGEUR_BLOCK_PX/*currentGb.height*/ <= NB_BLOCK_HAUTEUR*LARGEUR_BLOCK_PX) {
			if (isListeningKeyboard) {
				switch (event.keyCode) {
					/* --- flèche gauche --- */
					case  Keyboard.LEFT:
						move(currentGb, "L", true);
						break;
					/* --- flèche droite --- */
					case  Keyboard.RIGHT:
						move(currentGb, "R", true);
						break;
					/* --- flèche bas --- */
					case  Keyboard.DOWN:
						move(currentGb, "D", false);
						break;
					/* --- flèche haut --- */
					case  Keyboard.UP:
						currentGb.rotateBloc();
						break;
				}
			}
			
			/*timer.reset();
			timer.start();*/
		}
		
		/**
		 * fonction qui permet de bouger un bloc dans la grille en respectant les contraintes de collisions
		 * effectue le déplacement si possible
		 * retourne un boolean indiquant si le déplacement a eu lieu
		 * @param gb
		 * @param direction
		 * @param key indique on positionne l'objet si isMovable = false
		 */
		private function move(gb:GBlock, direction:String, key:Boolean = false):Boolean {
			
			// on détecte les collisions
			if (!grille.isMovable(gb, direction)) {
				// on peut pas bouger, on positionne
				if (!key) {
					positionneCurrentGBlock();
					generateBlock();					
				}
			} else {
				// on bouge le gblock
				var supX:int = 0;  
				var supY:int = 0;  
				switch (direction) {
					case "D":
						// DOWN
						//gb.y += LARGEUR_BLOCK_PX;		
						supY = LARGEUR_BLOCK_PX;
						currentGb.posY++;
						break;
					case "L":
						// LEFT
						//gb.x -= LARGEUR_BLOCK_PX;
						supX = -LARGEUR_BLOCK_PX
						currentGb.posX--;
						break;
					case "R":
						// RIGHT
						//gb.x += LARGEUR_BLOCK_PX;
						supX = LARGEUR_BLOCK_PX;
						currentGb.posX++;
						break;
				}
				/*Tweener.addTween(gb, {
					x: gb.x + supX,
					y: gb.y + supY,
					transition:"easeOutElastic",
					time:0.8
				});*/
				if (USER_TWEENER) {
					Tweener.addTween(gb, {
						x: gb.posX * LARGEUR_BLOCK_PX,
						y: gb.posY * LARGEUR_BLOCK_PX,
						transition:"linear",
						time:0.5
					});
				} else {
					gb.x = gb.posX * LARGEUR_BLOCK_PX;
					gb.y = gb.posY * LARGEUR_BLOCK_PX;
				}
				
			}	
			
			//trace("currentBloc : "+currentGb.x+" "+currentGb.y+" "+currentGb.z);
			
			return true;
		}
		
		/**
		 * permet d'inscrire le gbloc courant dans la grille
		 * cela signifie que le gbloc n'est plus controlable par l'utilisateur
		 */ 
		private function positionneCurrentGBlock():void {
			// on met à jour la grille
			// faut calculer le x et le y de la grille
			/*var x:int = currentGb.x / LARGEUR_BLOCK_PX;
			var y:int = currentGb.y / LARGEUR_BLOCK_PX;*/
			grille.positionner(currentGb);
			
			grille.deleteWinningLines();
			
			//trace(grille.toString());
			
			trace('positionner : '+currentGb.posX+", "+currentGb.posY);
		}
		
		public function removeBlock(obj:Block):void {
			// on a un bloc
			obj.deleteCube();
			removeChild(obj);
			obj.cube = null;
			obj = null;
			
		}
	}
	
}